﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Mesuro.Common {
	public struct Complex {
		/// <summary>
		/// The complex number `i` representing the square-root of -1.
		/// </summary>
		public static Complex I = new Complex( 0, 1 );

		public static Complex FromCartesian( double real, double imag ) {
			return new Complex( real, imag );
		}
		public static Complex FromPolar( double mag, double arg ) {
			return new Complex( mag * Math.Cos( arg ), mag * Math.Sin( arg ) );
		}

		private double re, im;

		/// <summary>
		/// Gets/Sets the real part of this number leaving the imaginary part unchanged.
		/// </summary>
		public double Re {
			get { return re; }
		}
		/// <summary>
		/// Gets/Sets the imaginary part of this number leaving the real part unchanged.
		/// </summary>
		public double Im {
			get { return im; }
		}

		/// <summary>
		/// Gets/Sets the magnitude of this number leaving the argument unchanged.
		/// </summary>
		public double Mag {
			get {
				return Math.Sqrt( Re * Re + Im * Im );
			}
		}
		/// <summary>
		/// Gets/Sets the argument of this number in radians leaving the magnitude unchanged.
		/// </summary>
		public double Arg {
			get {
				return Math.Atan2( Im, Re );
			}
		}

		/// <summary>
		/// Gets the complex conjugate of this number.
		/// </summary>
		public Complex Conjugate {
			get {
				return ~this;
			}
		}

		/// <summary>
		/// Indicates whether this number is a purely real number (Im(z)==0).
		/// </summary>
		public bool IsReal { get { return im == 0; } }
		/// <summary>
		/// Indicates whether this number is a purely imaginary number (Re(z)==0).
		/// </summary>
		public bool IsImaginary { get { return re == 0; } }

		/// <summary>
		/// Create a complex number with the specified cartesian values.
		/// </summary>
		/// <param seriesKey="real">The real part of the complex number</param>
		/// <param seriesKey="imag">The imaginary part of the complex number</param>
		public Complex( double real, double imag ) {
			re = real;
			im = imag;
		}

		#region Unary Operators (+,-,~)
		public static Complex operator +( Complex c ) {
			return c;
		}
		public static Complex operator -( Complex c ) {
			return new Complex( -c.Re, -c.Im );
		}
		/// <summary>
		/// Conjugate
		/// </summary>
		public static Complex operator ~( Complex c ) {
			return new Complex( c.Re, -c.Im );
		}
		#endregion Unary Operators

		#region Additive Operators (+,-)
		public static Complex operator +( Complex a, double b ) {
			return new Complex( a.Re + b, a.Im );
		}
		public static Complex operator -( Complex a, double b ) {
			return new Complex( a.Re - b, a.Im );
		}
		public static Complex operator +( double a, Complex b ) {
			return new Complex( a + b.Re, +b.Im );
		}
		public static Complex operator -( double a, Complex b ) {
			return new Complex( a - b.Re, -b.Im );
		}

		public static Complex operator +( Complex a, Complex b ) {
			return new Complex( a.Re + b.Re, a.Im + b.Im );
		}
		public static Complex operator -( Complex a, Complex b ) {
			return new Complex( a.Re - b.Re, a.Im - b.Im );
		}
		#endregion Additive Operators

		#region Multiplicative Operators (*,/)
		public static Complex operator *( Complex a, double b ) {
			return new Complex( a.Re * b, a.Im * b );
		}
		public static Complex operator /( Complex a, double b ) {
			return new Complex( a.Re / b, a.Im / b );
		}
		public static Complex operator *( double a, Complex b ) {
			return new Complex( a * b.Re, a * b.Im );
		}
		public static Complex operator /( double a, Complex b ) {
			return FromPolar( a / b.Mag, -b.Arg );
		}

		public static Complex operator *( Complex a, Complex b ) {
			return FromPolar( a.Mag * b.Mag, a.Arg + b.Arg );
		}
		public static Complex operator /( Complex a, Complex b ) {
			return FromPolar( a.Mag / b.Mag, a.Arg - b.Arg );
		}
		#endregion Multiplicative Operators

		#region Casts
		public static implicit operator Complex( double d ) {
			return new Complex( d, 0 );
		}
		public static explicit operator double( Complex c ) {
			if (c.Im == 0)
				return c.Re;
			throw new InvalidCastException( "Cannot cast complex(z) to real (unless Im(z)==0)." );
		}
		#endregion Casts

		public override string ToString() {
			StringBuilder sb = new StringBuilder();
			sb.Append( re );
			sb.Append( (im > 0) ? "+" : "-" ).Append( "i" );
			sb.Append( im );
			return sb.ToString();
		}
	}

	public static class CMath {
		/// <summary>
		/// The complex number `i` representing the square-root of -1.
		/// </summary>
		public static readonly Complex I = new Complex( 0, 1 );
		/// <summary>
		/// The mathematical constant `num` representing the natural base.
		/// </summary>
		public const double E = 2.71828182845904523536028747135266249775724709369995;
		/// <summary>
		/// The mathematical constant `π` representing the ratio of a circle'path circumference to it'path diameter.
		/// </summary>
		public const double PI = 3.1415926535897932384626433832795028841971693993751;

		public static double Re( Complex c ) { return c.Re; }
		public static double Im( Complex c ) { return c.Im; }
		public static double Abs( Complex c ) { return c.Mag; }
		public static double Arg( Complex c ) { return c.Arg; }

		public static Complex Sin( Complex c ) {
			if (c.IsReal)
				return Math.Sin( c.Re );
			if (c.IsImaginary)
				return I * Math.Sinh( c.Im );
			return new Complex( Math.Sin( c.Re ) * Math.Cosh( c.Im ), Math.Cos( c.Re ) * Math.Sinh( c.Im ) );
		}
		public static Complex Cos( Complex c ) {
			if (c.IsReal)
				return Math.Cos( c.Re );
			if (c.IsImaginary)
				return Math.Cosh( c.Im );
			return new Complex( Math.Cos( c.Re ) * Math.Cosh( c.Im ), -Math.Sin( c.Re ) * Math.Sinh( c.Im ) );
		}
		public static Complex Tan( Complex c ) {
			if (c.IsReal)
				return Math.Tan( c.Re );
			if (c.IsImaginary)
				return I * Math.Tanh( c.Im );
			return (Math.Tan( c.Re ) + Math.Tan( c.Im )) / (1 - Math.Tan( c.Re ) * Math.Tan( c.Im ));
		}

		public static Complex Pow( Complex b, Complex e ) {
			return Complex.FromPolar(
				Math.Pow( b.Mag, e.Re / 2 ) * Math.Exp( -e.Im * b.Arg ),
				e.Re * b.Arg + 0.5 * e.Im * Math.Log( b.Mag )
			);
		}
		public static Complex Pow( Complex b, double e ) {
			return Complex.FromPolar( Math.Pow( b.Mag, e / 2 ), e * b.Arg );
		}
		public static Complex Pow( double b, Complex e ) {
			return Complex.FromPolar( Math.Pow( b, e.Re / 2 ), 0.5 * e.Im * Math.Log( b ) );
		}

		public static Complex Sqrt( Complex c ) {
			return Pow( c, 0.5 );
		}
		public static Complex Sqrt( double c ) {
			if (c >= 0)
				return Math.Sqrt( c );
			else
				return I * Math.Sqrt( -c );
		}

		public static Complex Root( Complex e, Complex c ) {
			return Pow( c, 1 / e );
		}

		public static Complex Exp( Complex e ) {
			return Complex.FromPolar( Math.Exp( e.Re ), e.Im );
		}
		public static Complex Log( Complex e ) {
			return new Complex( Math.Log( e.Mag ), e.Arg );
		}
		public static decimal Log10( decimal num ) {
			int[] bytes = Decimal.GetBits( num );
			double m = bytes[0];
			m += bytes[1] * Math.Pow( 2d, 32 );
			m += bytes[2] * Math.Pow( 2d, 2 * 32 );
			decimal logM = (decimal)Math.Log10( m );
			return logM - ((bytes[3] & 0x00FF0000) >> 16);
		}

		public static Complex Round( Complex rho, int decimals ) {
			return new Complex( Math.Round( rho.Re, decimals ), Math.Round( rho.Im, decimals ) );
		}
	}
}
